#include <platform/interrupt.h>
#include <platform/cpu.h>
#include <platform/memory.h>
#include <string.h>
#include <assert.h>
#include <xbook/debug.h>
#include <xbook/schedule.h>
#include <xbook/thread.h>

static pid_t next_pid;

/* 初始化链表头 */

// 全局队列链表，用来查找所有存在的任务
LIST_HEAD(thread_global_list);

/* idle任务 */
thread_t *thread_kmain;

thread_t *thread_current;   /* 当前任务指针 */

/* 线程池 */
thread_t *thread_pool;

/**  
 * new_pid - 分配一个pid
 */
static pid_t new_pid()
{
    return next_pid++;
}

pid_t fork_pid()
{
    return new_pid();
}

thread_t *alloc_thread()
{
    int i;
    thread_t *thread;
    uint8_t *p = (uint8_t *) thread_pool;
    for (i = 0; i < NR_THREADS; i++) {
        thread = (thread_t *) p;
        if (thread->flags == 0) {
            thread->flags = THREAD_FLAG_ALLOCED;
            return thread;
        }
        p += THREAD_KSTACK_SIZE;
    }
    return NULL;
}

int free_thread(thread_t *_thread)
{
    int i;
    thread_t *thread;
    uint8_t *p = (uint8_t *) thread_pool;
    for (i = 0; i < NR_THREADS; i++) {
        thread = (thread_t *) p;
        if (thread == _thread && thread->flags != 0) {
            thread->flags = 0;
            return 0;
        }
        p += THREAD_KSTACK_SIZE;
    }
    return -1;
}


#if 0
/**  
 * roll_back_pid - 回滚一个pid
 */
static void roll_back_pid()
{
    --next_pid;
}
#endif

/**
 * thread_init - 初始化线程
 * @thread: 线程结构地址
 * @name: 线程的名字
 * @priority: 线程优先级
 */
void thread_init(thread_t *thread, char *name, int priority)
{
    memset(thread->name, 0, MAX_THREAD_NAMELEN);
    // 复制名字
    strcpy(thread->name, name);

    thread->state = THREAD_READY;
    
    // 修复优先级
    if (priority < 0)
        priority = 0;
    if (priority > MAX_PRIORITY_NR - 1)
        priority = MAX_PRIORITY_NR - 1;

    thread->priority = priority;
    thread->timeslice = 3;  /* 时间片大小默认值 */
    thread->ticks = thread->timeslice;  /* timeslice -> ticks */
    thread->elapsed_ticks = 0;    /* 总运行时间 */
    
    /* get a new pid */
    thread->pid = new_pid();
    thread->tgid = thread->pid; /* 默认都是主线程，需要的时候修改 */
    thread->parent_pid = -1;  /* no parent */
    thread->exit_status = 0;  /* no status */

    // set kernel stack as the top of thread mem struct
    thread->kstack = (unsigned char *)(((unsigned long )thread) + THREAD_KSTACK_SIZE);

    /* no priority queue */
    thread->prio_queue = NULL;

    /* thread stack magic */
    thread->stack_magic = THREAD_STACK_MAGIC;
}

void thread_free(thread_t *thread)
{
    /* remove from global list */
    list_del(&thread->global_list);
    free_thread(thread);
}

/**
 * thread_global_list_add - 把任务添加到全局队列
 * @thread: 任务
 */
void thread_global_list_add(thread_t *thread)
{
    // 保证不存在于链表中
    ASSERT(!list_find(&thread->global_list, &thread_global_list));
    // 添加到全局队列
    list_add_tail(&thread->global_list, &thread_global_list);
}

void thread_set_timeslice(thread_t *thread, uint32_t timeslice)
{
    if (thread) {
        if (timeslice < THREAD_MIN_TIMESLICE)
            timeslice = THREAD_MIN_TIMESLICE;
        if (timeslice > THREAD_MAX_TIMESLICE)
            timeslice = THREAD_MAX_TIMESLICE;
        thread->timeslice = timeslice;
    }
}

/**
 * find_thread_by_pid - 初始化任务的内存管理
 * @thread: 任务
 */
thread_t *find_thread_by_pid(pid_t pid)
{
    thread_t *thread;
    /* 关闭中断 */
    unsigned long flags;
    save_intr(flags);
    list_for_each_owner(thread, &thread_global_list, global_list) {
        if (thread->pid == pid) {
            restore_intr(flags);
            return thread;
        }
    }
    restore_intr(flags);
    return NULL;
}

/**
 * kthread_start - 开始一个线程
 * @name: 线程的名字
 * @func: 线程入口
 * @arg: 线程参数
 */
thread_t *kthread_start(char *name, int priority, thread_func_t *func, void *arg)
{
    // 创建一个新的线程结构体
    thread_t *thread = (thread_t *) alloc_thread();
    if (!thread)
        return NULL;
    
    // 初始化线程
    thread_init(thread, name, priority);
    
    //printk("alloc a thread at %x\n", thread);
    // 创建一个线程
    thread_make_stack(thread, func, arg);

    /* 操作链表时关闭中断，结束后恢复之前状态 */
    unsigned long flags;
    save_intr(flags);

    thread_global_list_add(thread);
    thread_priority_queue_add_tail(thread);
    
    restore_intr(flags);
    return thread;
}

/**
 * thread_activate - 激活任务
 * @thread: 要激活的任务
 */
void thread_activate(thread_t *thread)
{
    /* 任务不能为空 */
    ASSERT(thread != NULL);
    
    /* 设置为运行状态 */
    thread->state = THREAD_RUNNING;
    //printk("vmm:%x\n", thread->vmm);

}

/**
 * thread_block - 把任务阻塞
 */
void thread_block(thread_state_t state)
{
    // 先关闭中断，并且保存中断状态
    unsigned long flags;
    save_intr(flags);

    /*
    state有4种状态，分别是THREAD_BLOCKED, THREAD_WAITING, THREAD_STOPPED, THREAD_ZOMBIE
    它们不能被调度
    */
    ASSERT ((state == THREAD_BLOCKED) || 
            (state == THREAD_WAITING) || 
            (state == THREAD_STOPPED) ||
            (state == THREAD_HANGING) ||
            (state == THREAD_ZOMBIE));
    
    // 改变状态
    thread_t *current = current_thread;
    //printk(PART_TIP "thread %s blocked with status %d\n", current->name, state);
    current->state = state;
    
    // 调度到其它任务
    schedule();
    // 恢复之前的状态
    restore_intr(flags);
}

/**
 * thread_unblock - 解除任务阻塞
 * @thread: 要解除的任务
 */
void thread_unblock(thread_t *thread)
{
    // 先关闭中断，并且保存中断状态
    unsigned long flags;
    save_intr(flags);
#if 1
    if (!((thread->state == THREAD_BLOCKED) || 
        (thread->state == THREAD_WAITING) ||
        (thread->state == THREAD_STOPPED))) {
        panic("thread_unblock: thread name=%s pid=%d state=%d\n", thread->name, thread->pid, thread->state);
    }
#endif    
    /*
    state有3种状态，分别是THREAD_BLOCKED, THREAD_WAITING
    只有它们能被唤醒, THREAD_ZOMBIE只能阻塞，不能被唤醒
    */
    ASSERT((thread->state == THREAD_BLOCKED) || 
        (thread->state == THREAD_WAITING) ||
        (thread->state == THREAD_STOPPED));
    
    if (thread->state != THREAD_READY) {
        // 保证没有在就绪队列中
        ASSERT(!is_thread_in_priority_queue(thread));
        // 已经就绪是不能再次就绪的
        if (is_thread_in_priority_queue(thread)) {
            panic("TaskUnblock: thread has already in ready list!\n");
        }
        // 处于就绪状态
        thread->state = THREAD_READY;
        // 把任务放在最前面，让它快速得到调度
        thread_priority_queue_add_head(thread);
    }
    
    restore_intr(flags);
    /* 由于现在状态变成就绪，并且在队列里面，所以下次切换任务时就会切换到任务，并且往后面运行 */
}

/**
 * thread_yeild - 主动让出cpu
 * 
 */
void thread_yeild()
{
    // 先关闭中断，并且保存中断状态
    unsigned long flags;
    save_intr(flags);
    set_current_state(THREAD_READY); /* 设置为就绪状态 */
    schedule(); /* 调度到其它任务 */
    restore_intr(flags);
}

/**
 * create_kmain_thread - 创建内核主线程
 * 
 * 内核主线程就是从boot到现在的执行流。到最后会演变成idle
 * 在这里，我们需要给与它一个身份，他才可以进行多线程调度
 */
static void create_kmain_thread()
{
    // 当前运行的就是主线程
    thread_kmain = (thread_t *) (KERNEL_STATCK_TOP - THREAD_KSTACK_SIZE);
    thread_current = thread_kmain;   /* 设置当前任务 */
    
    /* 最开始设置为最佳优先级，这样才可以往后面运行。直到运行到最后，就变成IDLE优先级 */
    thread_init(thread_kmain, "kmain", THREAD_PRIO_RT);
    
    /* 设置为运行中 */
    thread_kmain->state = THREAD_RUNNING;
    thread_global_list_add(thread_kmain); /* 添加到全局任务 */
    
}

/**
 * sys_get_pid - 获取任务id
 */
pid_t sys_get_pid()
{
    /* 
    当调用者为进程时，tgid=pid
    当调用者为线程时，tgid=master process pid
    也就是说，线程返回的是主线程（进程）的pid
     */
    return current_thread->tgid;
}

/**
 * sys_get_ppid - 获取任务父进程id
 */
pid_t sys_get_ppid()
{
    return current_thread->parent_pid;
}

/**
 * sys_get_tid - 获取线程id
 */
pid_t sys_get_tid()
{
    /* 由于最小粒度是线程，所以，pid->线程id。 */
    return current_thread->pid;
}

/**
 * print_thread - 打印所有任务
 */
void print_thread()
{
    printk("\n----Task----\n");
    thread_t *thread;
    list_for_each_owner(thread, &thread_global_list, global_list) {
        printk("name %s pid %d ppid %d state %d\n", 
            thread->name, thread->pid, thread->parent_pid,  thread->state);
    }
}

void dump_thread(thread_t *thread)
{
    printk("----Task----\n");
    printk("name:%s pid:%d parent pid:%d state:%d\n", thread->name, thread->pid, thread->parent_pid, thread->state);
    //printk("vmm->vm_frame:%x priority:%d ticks:%d elapsed ticks:%d\n", thread->vmm->page_storage, thread->priority, thread->ticks, thread->elapsed_ticks);
    printk("exit code:%d stack magic:%d\n", thread->exit_status, thread->stack_magic);
}

void kthread_A(void *arg)
{
    printk(KERN_DEBUG "[thread]: A start...\n");
    while (1) {
        cpu_idle();
    }
}

void kthread_B(void *arg)
{
    printk(KERN_DEBUG "[thread]: B start...\n");
    while (1) {
        cpu_idle();
    }
}
void kthread_C(void *arg)
{
    printk(KERN_DEBUG "[thread]: C start...\n");
    while (1) {
        cpu_idle();
    }
}

void thread_user_entry(void *arg)
{
    printk(KERN_DEBUG "[user]: start...\n");
    while (1) {

    }
}

void init_user()
{
    /* 降级期间不允许产生中断 */
	unsigned long flags;
    save_intr(flags);
    /* 当前任务降级，这样，其它任务才能运行到 */
    thread_kmain->priority = THREAD_PRIO_IDLE;
    
    restore_intr(flags);
    /* 调度到更高优先级的任务允许 */
    schedule();
    /* 当没有任务运行时，就会继续执行这个地方 */
    printk(KERN_DEBUG "[thread]: idle start...\n");
    while (1) {
        cpu_idle();
    }
}

/**
 * init_threads - 初始化多任务环境
 */
void init_threads()
{
    /* 分配线程池 */
    thread_pool = mempool_alloc(THREAD_KSTACK_SIZE * NR_THREADS);
    if (thread_pool == NULL)
        panic("alloc thread pool failed!\n");

    memset(thread_pool, 0, THREAD_KSTACK_SIZE * NR_THREADS);
    
    init_schedule();

    next_pid = 0;
    /* 创建内核主线程 */
    create_kmain_thread();

    /* 有可能做测试阻塞main线程，那么就没有线程，
    在切换任务的时候就会出错，所以这里创建一个测试线程 */
    #if 0
    kthread_start("test", THREAD_PRIO_RT, kthread_A, "NULL");
    kthread_start("test2", THREAD_PRIO_RT, kthread_B, "NULL");
    kthread_start("test3", THREAD_PRIO_RT, kthread_C, "NULL");
    #endif
    //kthread_start("test4", 1, threadD, "NULL");
    
    kthread_start("user", THREAD_PRIO_USER, thread_user_entry, NULL);
    
    /* create kernel server */
    // kthread_start("thrdser", THREAD_PRIO_RT, thrdser_main, "NULL");
    // kthread_start("mmanser", THREAD_PRIO_RT, mmanser_main, "NULL");
    // kthread_start("timeser", THREAD_PRIO_RT, timeser_main, "NULL");
    // kthread_start("systmser", THREAD_PRIO_RT, systmser_main, "NULL");
    
    printk(KERN_INFO "[thread]: init done\n");
}
